import {Injectable} from '@angular/core';
import { Table, Dexie } from 'dexie';
import { DBService } from '../db/db.service';
import {Course, CourseCheck, CoursePage, CourseProgress, CourseTask, ExtendedCourse} from './Course';
import { v4 as uuidv4 } from 'uuid';

@Injectable({providedIn: 'root'})
export class CourseLoaderService {
    constructor(private dbService: DBService) {}
    courseTable: Table | undefined = undefined;
    loadDB(){
        if(!this.courseTable){
            this.courseTable = this.dbService.table('course');
        }
        return this.courseTable;
    }
    progressTable: Table | undefined = undefined;
    loadProgressDB(){
        if(!this.progressTable){
            this.progressTable = this.dbService.table('courseProgress');
        }
        return this.progressTable;
    }
    codeTable: Table | undefined = undefined;
    loadCodeDB(){
        if(!this.codeTable){
            this.codeTable = this.dbService.table('code');
        }
        return this.codeTable;
    }
    async courseList(): Promise<Course[]> {
        const result = await this.loadDB()?.toArray() ?? [];
        return result.map(x=>x.data);
    }
    async create(name: string, id: string | null = null): Promise<Course> {
        const newId = id ?? uuidv4().toString();
        const task = new CourseTask('新任务', '', new CourseCheck(1, '', ''), new CourseCheck(1, '', ''));
        const page = new CoursePage('新页面', '', '', '', [task]);
        const course = new Course(newId, name, [page]);
        await this.loadDB()?.put({id: id, name: course.name, data: course});
        return course;
    }
    async get(id : string): Promise<Course | undefined> {
        const courses = await this.loadDB()?.where('id')?.equals(id)?.toArray() ?? [];
        if (courses.length !== 0) {
            return courses[0].data;
        }
        return undefined;
    }
    async exists(id: string): Promise<boolean> {
        const courses = await this.loadDB()?.where('id')?.equals(id)?.toArray() ?? [];
        return courses.length > 0;
    }
    async import(courses: Course[]){
        // 其实加个版本字段就能有效避免反向升级版本
        // TODO 在覆盖时重置发生变化的用户预输入代码
        courses.forEach(course => {
            this.loadDB()?.put({id: course.id, name: course.name, data: course});
        });
    }
    async importProgress(data: {
        course: Course[],
        progress: {id: string, page:number, state:string, task:number}[],
        code: {code: string, course: string, input: string, page:number}[]
    }){
        await this.import(data.course);
        const finishedId = new Set<string>();
        data.progress.forEach(async p=>{
            // 条件覆盖进度
            const existed = await this.loadProgressDB()?.where('id').equals(p.id).toArray();
            if(existed.length > 0){
                const raw = existed[0];
                if(raw.page < p.page || (raw.page === p.page && raw.task <= p.task)){
                    await this.loadProgressDB()?.put(p);
                }
                else{
                    finishedId.add(p.id);
                }
            }else{
                await this.loadProgressDB()?.put(p);
            }
        });
        data.code.forEach(async c=>{
            if(!finishedId.has(c.course)){
                // 排除已经完成一定程度的课程
                await this.loadCodeDB()?.put(c);
            }
        });
    }
    async delete(id: string){
        await this.reset(id);
        await this.loadDB()?.delete(id);
    }
    async reset(id: string){
        await this.loadProgressDB()?.delete(id);
        this.dbService.table('code')?.where('[course+page]').between([id, Dexie.minKey], [id, Dexie.maxKey]).delete();
    }
    async extend(courses: Course[]): Promise<ExtendedCourse[]> {
        return await Promise.all(courses.map(async (course): Promise<ExtendedCourse> => {
            const results = await this.loadProgressDB()?.where('id')?.equals(course.id)?.toArray() ?? [];
            if (results.length !== 0) {
                return new ExtendedCourse(course, results[0]);
            }else{
                const progress = new CourseProgress(course.id);
                await this.loadProgressDB()?.put(progress);
                return new ExtendedCourse(course, progress);
            }
        }));
    }
    async getExtended(id: string): Promise<ExtendedCourse | undefined> {
        const course = await this.get(id);
        if(course){
            const result = await this.extend([course]);
            return result[0];
        }
        return undefined;
    }
    async save(course: Course): Promise<void> {
        this.loadDB()?.put({id: course.id, name: course.name, data: course});
    }
    async codeIndex(): Promise<{[course:string]:{[page: number]: string}}>{
        const result: {[course:string]:{[page: number]: string}} = {};
        for(const item of await this.loadCodeDB()?.toArray()){
            if(item.course in result){
                result[item.course][item.page] = item.code;
            }else{
                result[item.course] = {}
                result[item.course][item.page] = item.code;
            }
        }
        return result;
    }
}
